package com.ys.sboot.config.corecfg.exception;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;

@ControllerAdvice
public class ApiResponseBodyAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return returnType.getMethod().getReturnType().equals(ResponseResult.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        ResponseResult bodyResponse = (ResponseResult) body;

        HttpMessage.HttpMessageBuilder builder = HttpMessage.builder();

        builder.timestamp(new Date()).isSuccess(bodyResponse.isSuccess())
                .code(bodyResponse.getCode())
                .message(bodyResponse.getMessage());

        boolean isNullArray = isNullArray(returnType, bodyResponse);

        builder.data(isNullArray ? Collections.emptyList() : bodyResponse
                .getData());

        return builder.build();
    }

    private boolean isNullArray(MethodParameter returnType,
                                ResponseResult bodyResponse) {
        Type returnGenericType = returnType.getGenericParameterType();
        if (!(returnGenericType instanceof ParameterizedType)) {
            return false;
        }

        Type resultObjectType = ((ParameterizedType) returnType
                .getGenericParameterType()).getActualTypeArguments()[0];
        Class resultObjectClass = null;
        if (resultObjectType instanceof Class) {
            resultObjectClass = (Class) resultObjectType;
        } else if (resultObjectType instanceof ParameterizedType) {
            resultObjectClass = (Class) ((ParameterizedType) resultObjectType)
                    .getRawType();
        }

        if (resultObjectClass == null) {
            return false;
        }
        List<Class<? extends Collection>> classList = new ArrayList<>();
        classList.add(List.class);
        classList.add(Set.class);

        return (classList.contains(
                resultObjectClass) || resultObjectClass.isArray())
                && bodyResponse.getData() == null;

    }

}
